# Copyright (c) 2005,2006,2007 Dynamic Solutions Inc. (support@dynamic-solutions.com)
#
# This file is part of DataZoomer.
#
# DataZoomer is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 3 of the License, or
# (at your option) any later version.
#
# DataZoomer is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.

"""Provides formatting of page and control objects

Themes in DataZoomer make use of the visitor pattern.  Themes then
are concrete visitors that visit the control nodes, including composites,
constructed by the DataZoomer applications.

Using the visitor pattern here makes creating new themes trivial at
the expense of making adding new control objects hard.  We felt this
was a fair tradeoff since themes are much more likey to change
for every DataZoomer installation while controls may change but those
changes will probably coincide with new releases of the framework.
"""

from zoom import zoomer
import dzhtml, dzacl
from qml2 import qml
from dzutil import *
from dzapp import App
from attic.dzcontrol import *
from os.path import join, isfile, abspath, split, normpath
from re import findall, compile
from time import strftime
from attic.dzcontrol2 import HtmlDiv, HtmlList, HtmlTag

INCLUDES_RE = compile("<#INCLUDE name=(?P<filename>.+?)>")
DV_FORMS_RE = compile('<[form|FORM].*?action=["\']?<#DV>["\']?[a-zA-Z0-9 ="\']*?>(?!<input type=hidden name=sid value=")')
TAG_RE = compile('<#(?P<TAGNAME>.+?)>')

PAGE   = """<HTML>
<HEAD>
  <title><#PAGETITLE></title>
  <link rel="stylesheet" href="<#ROOT>/themes/default/default.css" />
  <link rel="shortcut icon" href="favicon.ico" type="image/x-icon" />
</HEAD>
<BODY>
  <center>
  <TABLE class=smalltext width=750 border=0 cellpadding=0 cellspacing=0>
    <div id="Header">
    <tr><td>

      <table width=100% border=0 cellpadding=0 cellspacing=0>
        <tr>
          <td width=64 align=center><img src="<#IMG>spacer.gif" width=64 height=3 border=0 alt=""></td>
          <td width=256 align=center><img src="<#IMG>spacer.gif" width=256 height=3 border=0 alt=""></td>
          <td width=100%  align=center><img src="<#IMG>spacer.gif" width=200  height=1 border=0 alt=""></td>
        </tr>
        <tr>
          <td><img class=logo src="<#IMG>bannerlogo.png" alt="logo"></td>
          <!-- <td width=256 align=center></td> -->
          <td valign=top colspan=2><div id="SysMenu"><#SYSTEMMENU></div><div id="SysMenu2"><#MAINMENU></div></td>
        </tr>
      </table>
    </td></tr>
    </div>
    <tr><td height=2><img src="<#IMG>spacer.gif" width=100% height=0 border=0 alt=""></td></tr>
    <tr><td><h1><#TITLE></h1><h2><#PAGETITLE></h2></td></tr>
    <tr><td><div id="breadcrumb"><#BREADCRUMB></div></td></tr>
    <tr><td height=12><img src="<#IMG>spacer.gif" width=100% height=0 border=0 alt=""></td></tr>
    <tr><td height=350 valign=top>
        <#MAINPANEL>
    </td></tr>
    <tr><td><img src="<#IMG>spacer.gif" height=40 width=100% alt=""></td></tr>
    <tr><td><center><#FOOTER></center></td></tr>
  </TABLE>

  </center>

</BODY>
</HTML>
"""

ONEPANEL = """
          <table border=0 class=leftpanel cellspacing=0 cellpadding=0 width=100%>
          <tr>
              <td><img src=<#IMG>spacer.gif width=5 height=1 alt=""></td>
              <td><img src=<#IMG>spacer.gif width=100% height=1 alt=""></td>
              <td><img src=<#IMG>spacer.gif width=5 height=1 alt=""></td>
          </tr>

          <tr>
              <td><img src=<#IMG>spacer.gif width=5 height=400 alt=""></td>
              <td width=100% valign=top><font color=green><#MESSAGE></font><font color=red><#WARNING></font><br><#PAGECONTENT></td>
              <td><img src=<#IMG>spacer.gif width=5 height=400 alt=""></td>
          </tr>
          </table>
"""

TWOPANEL = """
          <table border=0 class=leftpanel cellspacing=0 cellpadding=0 width=100%>
          <tr>
              <td><img src=<#IMG>spacer.gif width=0 height=1 alt=""></td>
              <td width=150 height=1><img src=<#IMG>spacer.gif width=160 height=1 alt=""></td>
              <td><img src=<#IMG>spacer.gif width=1 alt=""></td>
              <td><img src=<#IMG>spacer.gif width=10 height=1 alt=""></td>
              <td><img src=<#IMG>spacer.gif width=100% height=1 alt=""></td>
              <td><img src=<#IMG>spacer.gif width=0 height=1 alt=""></td>
          </tr>

          <tr>
              <td><img src=<#IMG>spacer.gif width=0 height=400 alt=""></td>
              <td width=100 valign=top>
               <div id="controllermenu"><#CONTROLLERMENU></div>
               <div id="appaction"><#APPACTIONS></div>
               <div id="controlleractions"><#CONTROLLERACTIONS></div>
               <#PARTS>
              </td>
              <td><img src=<#IMG>spacer.gif width=1 height=400 alt=""></td>
              <td><img src=<#IMG>spacer.gif width=10 height=400 alt=""></td>
              <td width=100% valign=top><font color=green><#MESSAGE></font><font color=red><#WARNING></font><#PAGECONTENT></td>
              <td><img src=<#IMG>spacer.gif width=0 height=400 alt=""></td>
          </tr>

          </table>
"""

THREEPANEL = """
          <table border=0 class=leftpanel cellspacing=0 cellpadding=0 width=100%>
          <tr>
              <td><img src=<#IMG>spacer.gif width=5 height=10 alt=""></td>
              <td width=100 height=10><img src=<#IMG>spacer.gif width=100 height=1 alt=""></td>
              <td><img src=<#IMG>spacer.gif width=1 alt=""></td>
              <td><img src=<#IMG>spacer.gif width=3 height=1 alt=""></td>
              <td><img src=<#IMG>spacer.gif width=100% height=1 alt=""></td>
              <td><img src=<#IMG>spacer.gif width=3 height=1 alt=""></td>
              <td><img src=<#IMG>spacer.gif width=1 alt=""></td>
              <td width=100 height=10><img src=<#IMG>spacer.gif width=100 height=1 alt=""></td>
              <td><img src=<#IMG>spacer.gif width=5 height=10 alt=""></td>
          </tr>

          <tr>
              <td><img src=<#IMG>spacer.gif width=5 height=400 alt=""></td>
              <td width=100 valign=top><#LEFTPANEL></td>
              <td><img src=<#IMG>spacer.gif width=1 height=400 alt=""></td>
              <td><img src=<#IMG>spacer.gif width=3 height=400 alt=""></td>
              <td width=100% valign=top><#MESSAGE></center><#PAGECONTENT></td>
              <td><img src=<#IMG>spacer.gif width=3 height=400 alt=""></td>
              <td><img src=<#IMG>spacer.gif width=1 height=400 alt=""></td>
              <td width=100 valign=top><#RIGHTPANEL></td>
              <td><img src=<#IMG>spacer.gif width=5 height=400 alt=""></td>
          </tr>
          </table>
"""


HEADER = """
<center><TABLE>
  <tr><td><a href="/"><img src="<#IMG>logo4.png" border="0" alt="DataZoomer"></a></td></tr>
  <tr><td><#SITELINKS></td></tr>
<table>
"""

FOOTER = """
</TABLE>
Copyright &copy; 2003-2007 Dynamic Solutions Inc.
</center>
"""

PANEL = """
<table class="nicetable">
  <tr><th class="nicetable"><#CAPTION></th></tr>
  <tr><td class="nicetable"><#BODY></td></tr>
</table>
"""

email_body = """
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<HTML>
<BODY>
<table width="100%">
 <tr>
  <td align="right">
  <img src="<#IMG>/bannerlogo.png" alt="banner logo">
  </td>
 </tr>
 <tr>
  <td align="left">
  <font face="verdana,sand-serif" size="2"><#MESSAGE></font>
  </td>
 </tr>
</table>
</BODY>
</HTML>
"""

def IncludeTemplate(filename,theme=zoomer.theme_name,backup_theme='default'):
    theme_path = '%s/themes/%s/templates' % (zoomer.site_path,theme)
    default_path = '%s/themes/%s/templates' % (zoomer.site_path,backup_theme)
    if not filename.find('.')>0: filename = '%s.htm' % (filename)

    f = abspath(join(theme_path,filename))
    if isfile(f): return open(f,'r').read()
    f = abspath(join(default_path,filename))
    if isfile(f): return open(f,'r').read()

    ext = filename[filename.find('.'):]
    filename = '%s.%s' % (filename[:filename.find('.')],ext.lower()=='.html' and 'htm' or 'html')

    f = abspath(join(theme_path,filename))
    if isfile(f): return open(f,'r').read()
    f = abspath(join(default_path,filename))
    if isfile(f): return open(f,'r').read()

    return 'Missing %s' % filename[:filename.find('.')]

def IncludeBuiltin(filename,theme=zoomer.theme_name,backup_theme='default'):
    theme_path = '%s/themes/%s/builtin' % (zoomer.site_path,theme)
    default_path = '%s/themes/%s/builtin' % (zoomer.site_path,backup_theme)

    if not filename.find('.')>0: filename = '%s.xml' % (filename)

    f = abspath(join(theme_path,filename))
    if isfile(f): return open(f,'r').read()
    f = abspath(join(default_path,filename))
    if isfile(f): return open(f,'r').read()

    return None

def ThemeTemplate(filename,view,theme=zoomer.theme_name):
    return TaggedView(IncludeTemplate(filename,theme,'default'),view)


class ThemeParts:
    def build_message(self,msg):
        return msg

    def build_warning(self,warn):
        return warn

    def build_view_links(self,views):
        return [(isinstance(view,View) and self.link('',view.caption,{'view':view.method}) or view) for view in views]

    def build_action_links(self,actions):
        return [(isinstance(action,Action) and self.link('',action.caption,{'action':action.method}) or action) for action in actions]

    def build_system_link(self,app):
        if app.name == zoomer.app.name:
            return self.selected_system_menu_link(app)
        else:
            return self.system_menu_link(app)

    def build_system_menu(self):
        top_app_names = [app.name for app in zoomer.user_apps if app.visible and app.system_app and app.name in zoomer.user_groups]
        if zoomer.authenticated:
            if 'login' in top_app_names:
                top_app_names.remove('login')
            if 'register' in top_app_names:
                top_app_names.remove('register')
        else:
            if 'logout' in top_app_names:
                top_app_names.remove('logout')
        separator = self.system_menu_separator()
        return separator.join([self.build_system_link(zoomer.apps[name]) for name in top_app_names])

    def build_menu_link(self,app):
        return app.name==zoomer.app.name and self.selected_menu_link(app) or self.menu_link(app)

    def build_main_menu(self):
        top_app_names = [app.name for app in zoomer.user_apps if app.visible and app.position<10 and not app.system_app and app.name in zoomer.user_groups]
        separator = self.main_menu_separator()
        return separator.join([self.build_menu_link(zoomer.apps[name]) for name in top_app_names])

    def build_breadcrumb(self,page):
        app = zoomer.app
        return '%s :: %s :: %s' % (
            link_to('Home',title='DataZoomer Home',app='home',con=''),
            link_to(app.title,title=app.title,app=app.name,con=''),
            link_to(page.title,title=page.title,app=app.name,con=zoomer.webvars.con,action=zoomer.webvars.action),)

    def build_footer(self):
        return 'Copyright &copy; 2006 Dynamic Solutions Inc.'

    def visit_textinput(self,control,result):
        result.append('<input type="text" value="%(value)s" size=%(size)s name="%(name)s"%(title)s>' % control)

    def visit_memoinput(self,control,result):
        result.append('<textarea cols=40 rows=4 name="%(name)s">%(value)s</textarea>' % control)

    def visit_textarea(self,control,result):
        result.append('<textarea class="large" name="%(name)s">%(value)s</textarea>' % control)

    def visit_dateinput(self,control,result):
        result.append('<input type="text" name="%(name)s" value="%(value)s" size=8 maxlength=8>' % control)

    def visit_numericinput(self,control,result):
        result.append('<input type="text" name="%(name)s" value="%(value)s" size=%(size)d maxlength=%(maxlength)d>' % control)

    def visit_hidden(self,control,result):
        result.append('<input type="hidden" name="%(name)s" value="%(value)s">' % control)

    def visit_radio(self,control,result):
        result.append('<input type="radio" name="%(name)s" value="%(value)s">' % control.__dict__)

    def visit_checkbox(self,control,result):
        result.append('<input type="checkbox" name="%(name)s" value="%(value)s" %(checked)s>' % control.__dict__)

    def visit_button(self,control,result):
        result.append('<input type="submit" class="std_button" name="%(name)s" value="%(caption)s">' % control)

    def visit_resetbutton(self,control,result):
        result.append('<input type="reset" class="std_button" name="%(name)s">' % control)

    def visit_form(self,control,result):
        tpl = '<form action="%(action)s" method=POST>%(content)s</form>'
        result.append(tpl % control.__dict__)

    def visit_recordview(self,control,result):
        content = Composite()
        for field in control.fields:
            content.append(Html('<tr><td valign="top">%s</td><tr><td valign="top">%s</td><tr>' % (field.name,field.value)))
        control.content = content
        tpl = '<table width="100%">%(content)s</table>'
        result.append(tpl % control.__dict__)

    def visit_html(self,control,result):
        result.append(control.content)

    def visit_tag(self,control,result):
        tag_type = control.tag_type.lower()
        attribute_text = ' '.join([('%s="%s"' % (key,control.attributes[key])) for key in control.attributes])
        if attribute_text:
            attribute_text = ' ' + attribute_text
        if tag_type in ['textarea','form']:
            result.append( '<%s%s>%s</%s>' % (tag_type,attribute_text,control.content,tag_type) )
        else:
            result.append( '<%s%s>' % (tag_type,attribute_text) )

    def visit_email_body(self,control,result):
        content = Tagged(email_body)
        content.IMG = '%s/themes/%s/images/' % (zoomer.site_url,self.name)
        content.MESSAGE = control.message
        result.append( str(content) )

    def render(self,control):
        result = []
        control.accept(self,result)
        return '\n'.join(result)


class Theme(ThemeParts):
    def __init__(self):
        self.name        = 'default'
        self.version     = '1.0'
        self.author      = 'Herb Lainchbury'
        self.date        = 'May 27, 2004'
        self.info        = "<i>'default' theme by %s</i>" % self.author
        self.header      = HEADER
        self.PAGE        = PAGE

    def page_subtitle(self,subtitle):
        return None

    def build_navcontainer_for(self,heading,navlinks,divid,listclass="verticallist"):
        return self.build_navcontainer(heading,navlinks,divid,listclass)

    def build_navcontainer(self,heading,navlinks,divid,listclass):
        navlist = HtmlList(_class=listclass)
        for link in navlinks:
            navlist.add(link)
        div = HtmlDiv(id=divid)
        htitle = HtmlTag('h3')
        htitle.add(heading)
        div.add(htitle.render())
        div.add(navlist.render())
        return div

    def build_controller_menu(self,menu):
        content = '<br/>'.join([str(x) for x in self.build_view_links(menu)])
        return SidePanel('Views',content)

    def build_application_actions(self,actions):
        content = '<br/>'.join([str(x) for x in self.build_view_links(actions)])
        return SidePanel('App Actions',content)

    def build_controller_actions(self,actions):
        content = '<br/>'.join([str(x) for x in self.build_view_links(actions)])
        return Html(content)

    def build_application_css_tag(self):
        try: appcss = open('%s.css' % zoomer.app.name).read()
        except: appcss = ''
        css = """
<style type="text/css">
%s
</style>"""
        return css % appcss

    def visit_view_test(self,view,result):
        result.append(view.text)

    def visit_action(self,action,result):
        attr = ['%s=%s' % (k,v) for k,v in action.attributes.items()]
        attr = (attr and '&amp;%s' % '&amp;'.join(attr) or '')
        x = {'method':action.method,'caption':action.caption,'sid':zoomer.sid,'app':zoomer.app.name,'attr':attr}
        result.append('<a href="?sid=%(sid)s&amp;app=%(app)s&amp;action=%(method)s%(attr)s">%(caption)s</a>' % x)

    def visit_menu(self,action,result):
        attr = ['%s=%s' % (k,v) for k,v in action.attributes.items()]
        attr = (attr and '&amp;%s' % '&amp;'.join(attr) or '')
        x = {'controller':action.method,'caption':action.caption,'sid':zoomer.sid,'app':zoomer.app.name,'attr':attr}
        result.append('<a href="?sid=%(sid)s&amp;app=%(app)s&amp;con=%(controller)s%(attr)s">%(caption)s</a>' % x)

    def visit_view(self,action,result):
        x = {'method':action.method,'caption':action.caption,'sid':zoomer.sid,'app':zoomer.app.name}
        result.append('<a href="?sid=%(sid)s&amp;app=%(app)s&amp;view=%(method)s">%(caption)s</a>' % x)

    def visit_tagged(self,control,result):
        t = control.content
        control.STYLE      = '%s/themes/%s/%s.css' % (zoomer.root_url,self.name,self.name)
        control.TEMPLATE   = '%s/themes/%s/templates/' % (zoomer.root_url,self.name)
        if not 'IMG' in control.__dict__:
            control.IMG        = '%s/themes/%s/images/' % (zoomer.root_url,self.name)
        control.SITELOGO   = zoomer.config.get('site','logo',control.IMG+'bannerlogo.png')
        control.OWNERNAME  = zoomer.site_owner
        control.OWNEREMAIL = zoomer.site_email
        result.append(dzhtml.hFill(t,control))

    def visit_wiki(self,control,result):
        qmls = qml()
        t = qmls.convert(control.content)
        save_content = control.content
        control.content = t
        self.visit_tagged(control,result)
        control.content = save_content

    def visit_panel(self,panel,result):
        result.append('<div class=panel><span class=captiontext>%(title)s</span><br>%(content)s</div>' % panel.__dict__)

    def visit_sidepanel(self,panel,result):
        result.append('<br><table width=100%% class=panel><tr><td class=captiontext>%(title)s</td></tr><tr><td class=panel>%(content)s</td></tr></table>' % panel.__dict__)

    def visit_form(self,control,result):
        tpl = """
        <form action="<#DZ>" method=POST>
        <input type=hidden name=sid value="<#SID>">
        <input type=hidden name=app value="<#APP>">
        <input type=hidden name=con value="%(con)s">
        <input type=hidden name=action value="%(action)s">
        %(content)s
        </form>"""
        result.append(tpl % control.__dict__)

    def visit_hidden(self,hidden,result):
        result.append('<input type=hidden name="%(name)s" value="%(value)s">' % hidden.__dict__)

    def visit_inputtext(self,control,result):
        result.append('<input type=text name="%(name)s" value="%(value)s">' % hidden.__dict__)

    def visit_pulldown(self,control,result):
        result.append('<select name="%s">' % control.name)
        if control.all:
            result.append('<option value="*">All %s' % control.label)
        for rec in control.itemlist:
            if control.selected and str(control.selected)==str(rec[0]):
                result.append('<option selected value="%s">%s' % (rec[0],rec[1]))
            else:
                result.append('<option value="%s">%s' % (rec[0],rec[1]))
        result.append('</select>')

    def visit_browser(self,control,result):

        result.append('<table width="100%" class="baselist">')

        if control.title:
            result.append('<tr><td colspan=%s><table cellpadding=0 border=0 cellpadding=0 cellspacing=0 width=100%%><tr><td width=100%%><BIG><STRONG>%s</STRONG></BIG></td><td>%s</td></tr></table></td></tr>' % \
                (len(control.dataset.columns),control.title,''))

        if control.fields:
            columns = control.dataset.columns.keep(control.fields)
        else:
            columns = control.dataset.columns

        for column in columns:
            result.append('<th class="baselist">')
            if column.name in control.labels:
                label = control.labels[column.name]
            else:
                label = column.label
            result.append(label)
#            if column.name == control.key_field:
#                result.append('*')
            result.append('</th>')
        if control.on_delete:
            result.append('<th class=recordtable width=1%>&nbsp;</td>')

        count = 0
        for row in control.dataset:
            count += 1
            if count % 2:
                rowclass = 'zrow2'
            else:
                rowclass = 'zrow1'

            first_column = 1
            result.append('<tr class=%s>' % rowclass)
            for column in columns:
                value = row.__getattr__(column.name)
                result.append('<td>')
                if first_column and control.key_field in control.dataset.columns.names() and control.on_click:
                    key = row.__getattr__(control.key_field)
                    result.append(link_to(str(value),action=control.on_click,key=key))
                else:
                    result.append(str(value))
                first_column = 0
                result.append('&nbsp;</td>')
            if control.on_delete:
                link = url_for(action=control.on_delete,key=key)
                result.append('<td><a href="%s"><img src="<#IMG>trash.png" border=0 height=13px width=13px alt="trash"></a></td>' % link)
            result.append('</tr>')
        result.append('</table>')

    def visit_tableview(self,tableview,result):
        t = tableview.table
        result.append('<table width=100% class=recordtable bgcolor=#E6F8DD cellpadding=2 cellspacing=0>')

        result.append('<tr><td colspan=%s><table class=hdr cellpadding=2 border=0 width=100%%><tr><td width=100%%><BIG><STRONG>%s</STRONG></BIG></td><td>%s</td></tr></table></td></tr>' % \
            (len(t.columns),tableview.title,''))

        cols = t.columns.keep(tableview.columns)

        for column in cols:
            result.append('<th class=recordtable>')
            result.append(column.name)
            if column.name == tableview.key_field:
                result.append('*')
            result.append('</th>')

        count = 0
        for row in t:
            count += 1
            if count % 2:
                rowclass = 'recordtableo'
            else:
                rowclass = 'recordtablee'

            first_column = 1
            result.append('<tr class=%s>' % rowclass)
            for column in cols:
                value = row.__getattr__(column.name)
                result.append('<td>')
                if first_column and tableview.key_field and tableview.on_click:
                    key = row.__getattr__(tableview.key_field)
                    result.append(self.link('',str(value),{'app':zoomer.app.name,'action':tableview.on_click,'key':key}))
                else:
                    result.append(str(value))
                first_column = 0
                result.append('</td>')
            result.append('</tr>')
        result.append('</table>')

    def visit_recordview(self,control,result):
        result.append('<table border=0 width=100%% class=recordtable>')
        for field in control.fields:
            if field.visible:
                if field.name.upper() in control.labels:
                    label = control.labels[field.name.upper()]
                else:
                    label = field.label
                result.append('<tr class=recordtable><td width=25%%>%s</td><td width=75%%>%s</td></tr>' % (label,field.value))
        result.append('</table>')

    def visit_recordedit(self,control,result):
        result.append('<table>')
        if not '_map' in control.__dict__: control._map = {}
        for field in control.fields:
            if field.visible:
                control_title = control._map.get(field.name,{}).get('title','')
                field.title = control_title and ' title="%s"' % control_title or ''
                editor = TextInput(field).render()
                if isinstance(field.value,Field):
                    field = field.value
                    if field.type=='Pulldown':
                        editor = Pulldown(field.name,field.select,field.value,all=0).render()
                elif field.type == 'CHAR':
                    editor = TextInput(field).render()
                elif field.type == 'TEXT':
                    editor = MemoInput(field).render()
                elif field.type == 'DATE':
                    editor = '%s (yyyy-mm-dd)' % (TextInput(field).render())

                result.append('<tr><td valign=top>%s:</td><td valign=top>%s</td></tr>' % (control._map.get(field.name,{}).get('label',field.name),editor))

        result.append('</table>')

    def visit_page(self,page,result):
        wiki = Tagged(self.PAGE)
        wiki.TITLE      = page.title
        wiki.SUBTITLE   = self.page_subtitle(page.subtitle) or page.subtitle
        wiki.ICON       = self.icon(page.icon)
        wiki.PAGETITLE  = zoomer.name
        wiki.SYSTEMMENU = self.build_system_menu()
        wiki.MAINMENU   = self.build_main_menu()
        wiki.MESSAGE    = self.build_message(page.message)
        wiki.WARNING    = self.build_warning(page.warning)
        wiki.PARTS      = page.parts
        wiki.BREADCRUMB = page.breadcrumb and self.build_breadcrumb(page) or ''
        wiki.CONTROLLERMENU = ''
        wiki.CONTROLLERACTIONS = ''
        wiki.APPACTIONS = ''
        wiki.APPCSS = zoomer.app.css and self.build_application_css_tag() or ''
        wiki.FOOTER = page.footer or self.build_footer()
        wiki.ACTIONS    = page.actions
        actions_menu = 0

        left_panel = []

        wiki.PAGECONTENT = str(page.content)

        if page.controller:
            if page.controller.menu:
                wiki.CONTROLLERMENU = self.build_controller_menu(page.controller.menu)
            if page.controller.actions and not isinstance(page.controller,App):
                wiki.CONTROLLERACTIONS = self.build_controller_actions(page.controller.actions)
                actions_menu = 1

        if zoomer.app.actions:
            wiki.APPACTIONS = self.build_application_actions(zoomer.app.actions)
            actions_menu = 1

        wiki.PANELLAYOUT = actions_menu and 'twopanel' or 'onepanel'
        if page.parts or (page.controller and page.controller.menu) or actions_menu:
            wiki.MAINPANEL = self.two_panel_template()
        else:
            wiki.MAINPANEL = self.one_panel_template()

        self.visit_tagged(wiki,result)

    def panel(self,caption,body):
        return PANEL.replace('<#CAPTION>',caption).replace('<#BODY>',body)

    def link(self,link,text,args=None):
        if ('?' in link):
            if not ('sid=' in link):
                newlink = link.replace('?','?sid=%s&amp;' % zoomer.sid)
            else:
                newlink = link
        else:
            newlink = '%s?sid=%s' % (link,zoomer.sid)
        if text==None: text = newlink
        if args:
            alist = ['%s=%s' % (key,args[key]) for key in args]
            newlink = newlink + '&amp;' + '&amp;'.join(alist)
        return '<a href="%s">%s</a>' % (newlink,text)

    def icon_link(self,app):
        target = url_for(app=app.name)
        link1 = self.link(target,'<img height=44 src="%s/images/icons/%s.png" alt="Run the %s application." border="0">' % (zoomer.root_url,app.iconname,app.name))
        link2 = self.link(target,app.title)
        return '<table border=0><tr><td><center>%s</center></td></tr><tr><td><center>%s</center></td></tr></table>' % (link1,link2)

    def icon(self,app):
        return '<img height=44 src="%s/themes/default/icons/%s.png" alt="" border="0">' % (zoomer.root_url,zoomer.app.iconname)

    def system_menu_link(self,app):
        return link_to(app.title,title=app.title,app=app.name,con='')

    def selected_system_menu_link(self,app):
        return app.title

    def menu_separator(self):
        return '&nbsp;|&nbsp;'

    def main_menu_separator(self):
        return '&nbsp;|&nbsp;'

    def system_menu_separator(self):
        return '&nbsp;|&nbsp;'

    def menu_link(self,app):
        return '<a href="?sid=%s&amp;app=%s">%s</a>' % (zoomer.sid,app.name,app.title)

    def selected_menu_link(self,app):
        return '<a href="?sid=%s&amp;app=%s"><font color=red>%s</font></a>' % (zoomer.sid,app.name,app.title)

    def view_menu_link(self,text,view_name):
        return '<a href="?sid=%s&amp;app=%s&amp;view=%s">%s</a>' % (zoomer.sid,app.name,view_name,text)

    def view_menu_selected(self,text):
        return text

    def view_menu_separator(self):
        return '<br>'

    def build_main_link(self,app):
        values = {'sid':zoomer.sid,'appname':app.name,'text':app.title}
        if app.name == zoomer.app.name:
            return '%(text)s' % values
        else:
            return '<a href="?sid=%(sid)s&amp;app=%(appname)s">%(text)s</a>' % values

    def build_submenu_link(self,item):
        values = {'sid':zoomer.sid,'appname':zoomer.app.name,'text':item[0],'action':item[1]}
        if zoomer.webvars.action == values['action']:
            return '%(text)s' % values
        else:
            return '<a href="?sid=%(sid)s&amp;app=%(appname)s&amp;action=%(action)s">%(text)s</a>' % values

    def build_submenu(self,app):
        return 'SubMenu'

    def build_left_panel(self,app):
        return 'Left Panel'

    def message(self,msg):
        return '<center><font face="arial" color="red">%s</font></center>' % msg

    def fill(self,view):
        return dzhtml.hFill(view.content,view)

    def replace_tag(self,tagname,options={}):
        if self.__dict__.has_key('view'):
            if self.view.__dict__.has_key(tagname):
                return self.view.__dict__[tagname]
            else:
                return '[%s missing]' % tagname
        else:
            return '[%s,no view]' % tagname

    def page(self,page):
        tpl = self.page_template()
        return self.fill(tpl)

    def page_template(self):
        return PAGE

    def one_panel_template(self):
        return ONEPANEL

    def two_panel_template(self):
        return TWOPANEL

    def three_panel_template(self):
        return THREEPANEL


class DataViewTheme(Theme):
    include = "<#INCLUDE name=%s>"
    tag = "<#%s>"

    def __init__(self):
        Theme.__init__(self)
        self.dv1compatibility = 0

    def _includes(self,control):
        for match in INCLUDES_RE.findall(control.content):
            inc = TaggedView(IncludeTemplate(match,self.name,'shared'),control.view)
            control.content = control.content.replace(self.include%match,inc.render())
        return control.content

    def __dvtagcompatibility(self,content):
        hidden_sid = '<input type=hidden name=sid value="%s">' % zoomer.sid
        done=[]
        #add sid to DV forms
        for dvform in DV_FORMS_RE.findall(content):
            if dvform not in done:
                content = content.replace(dvform, '%s%s' % (dvform,hidden_sid))
                done.append(dvform)
        #<#DV>?
        content = content.replace('<#DV>?', '<#DV>&amp;')
        return content

    def __tryfill(self,text,obj):
        #fill the template with the object while maintaining unknown tags for higher levels
        tag = '<#%s>'

        if self.dv1compatibility==1: text = self.__dvtagcompatibility(text)

        done = []
        for match in TAG_RE.findall(text):
            orig_match = match
            # handle _FIELDNAME: convert _TAGNAME to TAGNAME
            #   this is not really needed for Dv2.x, but is kept for compatibility with Dv1.7
            if orig_match[:1]=='_': match = orig_match[1:]
            # handle FIELD tag with 'name' argument
            if orig_match[:11].upper()=='FIELD NAME=': match = orig_match.split('=')[1]
            if not orig_match in done:
                done.append(orig_match)
                try: repl = '%s' % obj.__dict__[match]
                except:
                    try:
                        if obj.__getattr__(match)==None: repl=None
                        else: repl = '%s' % obj.__getattr__(match)
                    except: repl = tag%match
                if not repl==None:
                    text = text.replace(tag%orig_match,repl)
        return text

    def visit_page(self,page,result):
        wiki = Tagged(self.PAGE)
        wiki.TITLE      = page.title
        wiki.SUBTITLE   = self.page_subtitle(page.subtitle) or page.subtitle
        wiki.PAGETITLE  = zoomer.name
        wiki.MESSAGE    = self.build_message(page.message)
        wiki.WARNING    = self.build_warning(page.warning)
        wiki.MAINPANEL  = self.one_panel_template()
        wiki.PAGECONTENT = str(page.content)
        wiki.SYSTEMMENU = self.build_system_menu()
        wiki.MAINMENU   = self.build_main_menu()
        wiki.ICON       = self.icon(page.icon)
        wiki.PARTS      = page.parts

        if page.views:
            page.parts.append(self.build_navcontainer_for('Views',self.build_view_links(page.views),divid='viewsdiv'))

        if page.actions:
            page.parts.append(self.build_navcontainer_for('Actions',self.build_action_links(page.actions),divid='actionsdiv'))

        self.visit_tagged(wiki,result)

    def one_panel_template(self):
        return "<#PAGECONTENT>"

    def visit_tagged(self,control,result):
        if self.dv1compatibility==1: control.content = self.__dvtagcompatibility(control.content)
        # to add the ability for legacy DataView templates to
        #   optionally use the DataZoomer style sheet as well as there own
        if not 'DZSTYLESHEET' in control.__dict__: control.DZSTYLESHEET = ''
        if not 'PAGETITLE' in control.__dict__:
            control.PAGETITLE = (zoomer.webvars.pageid and zoomer.webvars.pageid or 'Page')
        control.THEME = self.name
        control.STYLE = '/themes/%s/%s.css' % (self.name,self.name)
        if not 'IMG' in control.__dict__:
            control.IMG = '/themes/%s/images/' % self.name
        control.USERNAME = zoomer.session.loginid
        control.USERID = str(zoomer.session.userid).strip()
        control.DATETIME = strftime('%d %B %Y %H:%M:%S',now.timetuple())
        control.DATE = strftime('%d %B %Y',now.timetuple())
        control.PRODUCTNAME = (zoomer.webvars.product and zoomer.webvars.product.capitalize() or '<#PRODUCTNAME>')
        control.DV = url_for(app='dataview',con='dataview')
        control.OWNERNAME  = zoomer.site_owner
        control.OWNEREMAIL = zoomer.site_email

        result.append(dzhtml.hFill(control.content,control))

    def visit_taggedobject(self,control,result):
        result.append(self.__tryfill(control.content,control.obj))

    def visit_taggedview(self,control,result):
        control.content = self._includes(control)
        self.visit_tagged(control,result)


